﻿#include "stdafx.h"
#if _QG_USE_D11
#include "qg_d11_dev.h"
#include "qg_d11_mtrl.h"
#include "qg_trace.h"

//
bool D11Device::_BindShader() throw()
{
	D11Shader* s = (D11Shader*)_sprn->m_shd;

	if (_sprn->bd_shd)
	{
		_sprn->bd_shd = false;

		if (!s->_linked)
		{
			// 링크 안된 넘이다
			kobj_tracet(KLV_WARNING, KERR_NOREF, "no linked shader specified. will be linking instead.");
			kobj_tracef(KLV_WARNING, "target shader is '%s'", s->_key.Data());

			if (!s->Link())
				return false;
		}

		if (_vprn.m_shd_ps != s->_ps)
		{
			_vprn.m_shd_ps = s->_ps;
			_context->PSSetShader(s->_ps, NULL, 0);
		}

		if (_vprn.m_shd_vs != s->_vs)
		{
			_vprn.m_shd_vs = s->_vs;
			_context->VSSetShader(s->_vs, NULL, 0);
		}
	}

	// 매핑
	s->_ProcessMap(_context);

	return true;
}

//
qgShader* D11Device::_RoamImplShader(const char* name)
{
	D11Shader* shd = kobj_new(D11Shader);

	if (name)
		shd->_key = name;
	else
	{
		ksize_t n = k_nth();
		shd->_key.Format("d11_shader_%Lu", n);
	}

	return shd;
}


//////////////////////////////////////////////////////////////////////////
// 세이더
KIMPL_SUBCLASS(D11Shader, "D11Shader");

// include 구문 처리용, 디바이스 마운트에서 파일 가져오기
class D11Shader::ReadInclude : public ID3DInclude
{
private:
	D11Device* _dev;

public:
	ReadInclude(D11Device* dev)
		: _dev(dev)
	{
	}

	~ReadInclude()
	{
	}

	K_OVR HRESULT _stdcall Open(D3D_INCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID *ppData, UINT *pBytes)
	{
		*ppData = _dev->FileRead(pFileName, (kint*)pBytes);

		return S_OK;
	}

	K_OVR HRESULT _stdcall Close(LPCVOID pData)
	{
		k_delete(pData);

		return S_OK;
	}
};

//
void D11Shader::TypeInitialize(kType type)
{
	_InitAutoFunc();
}

//
D11Shader::D11Shader() throw()
{
}

//
D11Shader::~D11Shader() throw()
{
	if (_node_desc)
	{
		ID3D11InputLayout* il = (ID3D11InputLayout*)_node_desc;
		il->Release();
	}

	_RemoveData((IUnknown**)&_ps, &_dps, false);
	_RemoveData((IUnknown**)&_vs, &_dvs, false);
}

//
void D11Shader::_PrintError(ID3DBlob* errs, kint errcode, const char* target)
{
	kobj_tracef(KLV_ERROR, "%s with shader '%s'", k_errmsg(errcode), target);

	if (KLV_ERROR >= k_getlv() && errs)
	{
		if (errs->GetBufferSize() > 0)
		{
			const char* psz = (const char*)errs->GetBufferPointer();
			k_mesg(psz);
		}
	}
}

//
bool D11Shader::_CreatePixelShader(kconstpointer data, kint size, D3D_SHADER_MACRO* mcs, ReadInclude& inc, DWORD dw, const char* filename) throw()
{
	D11Device* dev = (D11Device*)_dev;
	const char* target = dev->GetProfilePs();

	ID3DBlob* blob = NULL;
	ID3DBlob* errs = NULL;
	HRESULT hr;
	kint ret;

	_RemoveData((IUnknown**)&_ps, &_dps, true);

	//
	hr = D11Device::D3DCOMPILE(data, size, filename, mcs, &inc, "PS",
		target, dw, 0, &blob, &errs);

	if (FAILED(hr))
		ret = KERR_PARSE;
	else
	{
		ksize_t blobsize = blob->GetBufferSize();
		kpointer blobdata = blob->GetBufferPointer();

		hr = dev->GetDevice()->CreatePixelShader(blobdata, blobsize, NULL, &_ps);

		if (FAILED(hr))
			ret = KERR_FAIL_CREATE;
		else
		{
			// 성공
			ret = KERR_SUCCESS;

			_dps = _AnalyzeData(QGSHADER_PS, blobdata, blobsize, false, false);
		}
	}

	//
	if (ret == KERR_SUCCESS)
	{
		kunk_release(blob);
		return true;
	}
	else
	{
		_PrintError(errs, KERR_PARSE, target);
		kunk_release(blob);
		kunk_release(errs);
		return false;
	}
}

//
bool D11Shader::_CreateVertexShader(kconstpointer data, kint size, D3D_SHADER_MACRO* mcs, ReadInclude& inc, DWORD dw, const char* filename) throw()
{
	D11Device* dev = (D11Device*)_dev;
	const char* target = dev->GetProfileVs();

	ID3DBlob* blob = NULL;
	ID3DBlob* errs = NULL;
	HRESULT hr;
	kint ret;

	_RemoveData((IUnknown**)&_vs, &_dvs, true);

	//
	hr = D11Device::D3DCOMPILE(data, size, filename, mcs, &inc, "VS",
		target, dw, 0, &blob, &errs);

	if (FAILED(hr))
		ret = KERR_PARSE;
	else
	{
		ksize_t blobsize = blob->GetBufferSize();
		kpointer blobdata = blob->GetBufferPointer();

		hr = dev->GetDevice()->CreateVertexShader(blobdata, blobsize, NULL, &_vs);

		if (FAILED(hr))
			ret = KERR_FAIL_CREATE;
		else
		{
			// 성공
			ret = KERR_SUCCESS;

			_dvs = _AnalyzeData(QGSHADER_VS, blobdata, blobsize, true, true);
		}
	}

	//
	if (ret == KERR_SUCCESS)
	{
		kunk_release(blob);
		return true;
	}
	else
	{
		_PrintError(errs, KERR_PARSE, target);
		kunk_release(blob);
		kunk_release(errs);
		return false;
	}
}

//
D3D_SHADER_MACRO* D11Shader::_MakeDefinition(ConditionArray& conds)
{
	if (conds.IsEmpty())
		return NULL;

	D3D_SHADER_MACRO* mcs = k_new(conds.Count() + 1, D3D_SHADER_MACRO);
	D3D_SHADER_MACRO* p = mcs;

	kForEach(auto& str, conds)
	{
		p->Name = str.Data();
		p->Definition = "1";	// 문자열은 상수니깐 설정해도 괜찮다.
		p++;
	}

	// 마지막은 NULL로 끝나지 않으면 안된다고 하더군
	p->Name = NULL;
	p->Definition = NULL;

	return mcs;
}

//
DWORD D11Shader::_MakeFlags(kint v)
{
	DWORD dw = D3DCOMPILE_PACK_MATRIX_ROW_MAJOR;

#if _SB_DEBUG_
	dw |= D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
#else
	dw |= D3DCOMPILE_OPTIMIZATION_LEVEL3;
#endif

	// DX9 때부터 써오던 플래그... 인데 필요함?
#if 0
	if (v & 0x0001)
		dw |= D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR;

	if (v & 0x0002)
		dw |= D3DCOMPILE_PACK_MATRIX_ROW_MAJOR;
#endif

	if (v & 0x0004)
		dw |= D3DCOMPILE_PARTIAL_PRECISION;

	return dw;
}

//
void D11Shader::_RemoveData(IUnknown** unk, BufferData** pbd, bool reset)
{
	if (*unk)
	{
		(*unk)->Release();

		if (reset)
			(*unk) = NULL;
	}

	//
	BufferData* bd = *pbd;

	if (!bd || k_atomdec(&bd->ref) != 0)
	{
		if (reset)
			(*pbd) = NULL;
	}
	else
	{
		if (bd->sr)
			bd->sr->Release();

		if (bd->maps.IsHave())
		{
			kForEach(MapBuffer* m, bd->maps)
			{
				m->buf->Release();
				delete m;
			}
		}

		delete bd;

		if (reset)
			(*pbd) = NULL;
	}
}

//
D11Shader::BufferData* D11Shader::_AnalyzeData(qgShaderType type, kpointer data, ksize_t size, bool keep, bool parseinput)
{
	BufferData* bd;
	D11Device* dev = (D11Device*)_dev;
	HRESULT hr;
	UINT i, n;

	bd = new BufferData();
	bd->ref = 1;

	// 보관
	if (keep)
	{
		bd->keep.Resize((kint)size);
		bd->keep.FillData((kbyte*)data, (kint)size);
	}

	// 리플렉션...이라는군
	ID3D11ShaderReflection* sr;
	D3D11_SHADER_DESC desc;

#if _MSC_VER && _MSC_VER < 1800
	static const GUID GUID_ID3D11ShaderReflection = {0x8d536ca1, 0x0cca, 0x4956, {0xa8, 0x37, 0x78, 0x69, 0x63, 0x75, 0x55, 0x84}};

	if (FAILED(D11Device::D3DREFLECT(data, size, GUID_ID3D11ShaderReflection, (void**)&sr)) ||
		FAILED(sr->GetDesc(&desc)))
#else
	if (FAILED(D11Device::D3DREFLECT(data, size, __uuidof(ID3D11ShaderReflection), (void**)&sr)) ||
		FAILED(sr->GetDesc(&desc)))
#endif
	{
		delete bd;
		return NULL;
	}

	bd->sr = sr;

	// 입력 파라미터 조사... 인데 필요할까...
	if (parseinput)
	{
		//-- 해야지...
		for (i = 0; i < desc.InputParameters; i++)
		{
			D3D11_SIGNATURE_PARAMETER_DESC prm;
			sr->GetInputParameterDesc(i, &prm);

			//k_mesgf("* %d -> %s\n", i, prm.SemanticName);
		}
	}

	// MapBuffer.buf 버퍼 만들 준비 사전 작업
	D3D11_BUFFER_DESC cbd;
	cbd.Usage = D3D11_USAGE_DYNAMIC;
	cbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	cbd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	cbd.MiscFlags = 0;
	cbd.StructureByteStride = 0;

	// 상수 버퍼 만들고, 상수 매퍼 만들기
	bd->maps.Resize(desc.ConstantBuffers);

	for (n = 0; n < desc.ConstantBuffers; n++)
	{
		ID3D11ShaderReflectionConstantBuffer* cb = sr->GetConstantBufferByIndex(n);
		D3D11_SHADER_BUFFER_DESC sbd;
		cb->GetDesc(&sbd);

		// 버퍼 만들기
		ID3D11Buffer* buf;

		cbd.ByteWidth = sbd.Size;
		hr = dev->GetDevice()->CreateBuffer(&cbd, NULL, &buf);

		if (FAILED(hr))
		{
			// 헐 버퍼 만들기 실패
			kobj_tracep(KLV_ERROR, KERR_FAIL_CREATE);
			continue;
		}

		// 버퍼 맵 만들기
		MapBuffer* pm = new MapBuffer();
		pm->buf = buf;
		pm->desc = sbd;

		//
		UINT cvar = 0;
		qgShaderVar* vars = k_new(sbd.Variables, qgShaderVar);

		for (i = 0; i < sbd.Variables; i++)
		{
			ID3D11ShaderReflectionVariable* rv = cb->GetVariableByIndex(i);
			D3D11_SHADER_VARIABLE_DESC svd;
			rv->GetDesc(&svd);

			if ((svd.uFlags&D3D_SVF_USED) == 0)
			{
				// 사용하지 않은 변수는 패스한다... 
				// 어짜피 사용안하는 값은 설정해 봤자지... 
				// 버퍼가 DYNAMIC 이더라도 별도의 ZERO 버퍼를 갖고 있으니까
				// MapBuffer.data[] 에 다 설정됨
				continue;
			}

			ID3D11ShaderReflectionType* rt = rv->GetType();
			D3D11_SHADER_TYPE_DESC std;
			rt->GetDesc(&std);

			// 변수 만들기
			qgShaderVar& v = vars[cvar++];
			v.name = svd.Name;
			v.hash = v.name.Hash(true);
			v.offset = (kint)svd.StartOffset;
			v.size = (kint)svd.Size;
			v.data = _ShaderTypeToShaderData(std);
			_MatchAutoFunc(v);

			//-- 실제 HLSL 데이터들은 정렬 패딩 들어가는데, 어떻게 할까?
		}

		// 실제 사용하는 변수를 넣음
		if (cvar > 0)
		{
			pm->vars.Resize(cvar);
			for (i = 0; i < cvar; i++)
				pm->vars[i] = vars[i];
		}

		k_delete(vars);

		// 버퍼 맵 저장
		bd->maps[n] = pm;
	}

	return bd;
}

//
bool D11Shader::_InternalBindData(qgShaderType type, kconstpointer data, kint size, kint flags, const char* filename) throw()
{
	D3D_SHADER_MACRO* mcs = _MakeDefinition(_conds);
	ReadInclude inc((D11Device*)_dev);
	DWORD dw = _MakeFlags(flags);

	//
	bool ret;

	switch (K_CAST_ENUM(type))
	{
		case QGSHADER_PS:
			ret = _CreatePixelShader(data, size, mcs, inc, dw, filename);
			K_UFLAG(&_insts, QGSHDI_PS, ret);
			break;

		case QGSHADER_VS:
			ret = _CreateVertexShader(data, size, mcs, inc, dw, filename);
			K_UFLAG(&_insts, QGSHDI_VS, ret);
			break;

		default:
			ret = false;
			break;
	}

	//
	k_delete(mcs);

	if (!ret)
		return false;
	else
	{
		// 링크 다시해!
		_linked = false;
		return true;
	}
}

//
bool D11Shader::BindFile(qgShaderType type, const char* filename, kint flags) throw()
{
	kint readsize;
	tpl::ScopedMemPtrAlloc<kpointer> readdata(_dev->FileRead(filename, &readsize));

	k_return_value_if_fail(readdata, false);

	return _InternalBindData(type, readdata, readsize, flags, filename);
}

//
bool D11Shader::BindData(qgShaderType type, kconstpointer data, kint size, kint flags) throw()
{
	k_return_value_if_fail(data && size > 0, false);

	return _InternalBindData(type, data, size, flags, _key.Data());
}

//
bool D11Shader::BindShader(qgShaderType type, const char* name) throw()
{
	k_return_value_if_fail(name, 0);

	// 찾는다
	KeyType key(name);

	D11Shader* shd = (D11Shader*)((D11Device*)_dev)->_RoamFind(QGROAM_MTL_2, &key);

	k_return_value_if_fail(shd, 0);

	// 찾는게 있으면 세이더를 참조해둔다
	bool ret;

	switch (K_CAST_ENUM(type))
	{
		case QGSHADER_PS:
			_RemoveData((IUnknown**)&_ps, &_dps, true);

			if (!shd->_ps)
				ret = false;
			else
			{
				_ps = shd->_ps;
				_ps->AddRef();

				_dps = shd->_dps;
				k_atominc(&_dps->ref);

				ret = true;
			}

			K_UFLAG(&_insts, QGSHDI_PS, ret);
			break;

		case QGSHADER_VS:
			_RemoveData((IUnknown**)&_vs, &_dvs, true);

			if (!shd->_vs)
				ret = false;
			else
			{
				_vs = shd->_vs;
				_vs->AddRef();

				_dvs = shd->_dvs;
				k_atominc(&_dvs->ref);

				ret = true;
			}

			K_UFLAG(&_insts, QGSHDI_VS, ret);
			break;

		default:
			ret = false;
			break;
	}

	if (!ret)
		return false;
	else
	{
		// 링크 다시해!
		_linked = false;
		return true;
	}
}

//
bool D11Shader::Link()
{
	k_return_value_if_ok(_linked, true);

	if (!K_OFLAG(_insts, QGSHDI_PS | QGSHDI_VS))
	{
		// D11은 ps, vs 둘다 있어야 한다
		kobj_tracep(KLV_ERROR, KERR_NOREF);
		return false;
	}

	_linked = true;

	return true;
}

//
qgShaderVar* D11Shader::GetVars(kint stage, kint* count)
{
	// 이렇게 해도 될것인가! 속도는 우짜지!
	kint cntps = _dps->maps.Count();
	kint cntvs = _dvs->maps.Count();
	kint cntgs = 0;

	tpl::Array<qgShaderVar>* ptr;

	if (stage < 0)
		ptr = NULL;
	else if (stage < cntps)
		ptr = &_dps->maps[stage]->vars;
	else if (stage < (cntps + cntvs))
		ptr = &_dps->maps[stage - cntps]->vars;
	else if (stage < (cntps + cntvs + cntgs))
		ptr = NULL;	 // 현재 GS가 없으니깐
	else
		ptr = NULL;

	if (!ptr)
	{
		if (count)
			*count = 0;
		return NULL;
	}
	else
	{
		if (count)
			*count = ptr->Count();
		return ptr->Data();
	}
}

//
void D11Shader::_ProcessMap(ID3D11DeviceContext* ctx) throw()
{
	ID3D11Buffer* bs[16];
	UINT cnt;

	if (_dps->maps.IsHave())
	{
		cnt = _UploadShader(_dps, bs, 15, ctx);
		if (cnt > 0)
			ctx->PSSetConstantBuffers(0, cnt, bs);
	}

	if (_dvs->maps.IsHave())
	{
		cnt = _UploadShader(_dvs, bs, 15, ctx);
		if (cnt > 0)
			ctx->VSSetConstantBuffers(0, cnt, bs);
	}
}

//
UINT D11Shader::_UploadShader(BufferData* bd, ID3D11Buffer** bs, UINT maxbs, ID3D11DeviceContext* ctx) throw()
{
	k_assert_neq(bd->maps.Count(), 0);

	//
	UINT cnt = 0;
	D3D11_MAPPED_SUBRESOURCE ms;

	// 맵 데이터 루프
	kForEach(MapBuffer* m, bd->maps)
	{
		if (m->vars.IsEmpty() ||
			FAILED(ctx->Map(m->buf, 0, D3D11_MAP_WRITE_DISCARD, 0, &ms)))
			continue;

		if (_cbintr)
		{
			// 세이더 변수 루프
			kForEach(qgShaderVar& v, m->vars)
			{
				v.ptr = (kbyte*)ms.pData + v.offset;

				if (v.role == QGSHDR_AUTO)
					((qgShaderAutoFunc)v.intr)(_dev, v.ptr, v);
			}

			// 사용자 지정
			_cbintr(m->vars.Count(), m->vars.Data(), this);
		}
		else
		{
			// 세이더 변수 루프
			kForEach(qgShaderVar& v, m->vars)
			{
				if (v.role == QGSHDR_AUTO)
					((qgShaderAutoFunc)v.intr)(_dev, (kbyte*)ms.pData + v.offset, v);
			}
		}

		// 해제하고
		ctx->Unmap(m->buf, 0);

		// 성공하면 추가
		bs[cnt++] = m->buf;

		if (cnt == maxbs)
		{
			// maxbs가 한계다!!!
			return maxbs;
		}
	}

	return cnt;
}

//
qgShaderData D11Shader::_ShaderTypeToShaderData(const D3D11_SHADER_TYPE_DESC& std)
{
	if (std.Class == D3D_SVC_STRUCT)
	{
		// 우왕... 우짜지. 구조체 못함. 왜이리 기능이 많아졌음...
		return QGSHDD_UNKNOWN;
	}

	switch (std.Type)
	{
		case D3D_SVT_BOOL: case D3D_SVT_UINT8:
			//-- 테스트 필요
			switch (std.Columns)
			{
				case 1:
					return QGSHDD_BYTE1;

				case 2:
					return QGSHDD_BYTE2;

				case 3:
					return QGSHDD_BYTE3;

				case 4:
					return QGSHDD_BYTE4;
			}
			break;

		case D3D_SVT_INT:
			//-- 테스트 필요
			switch (std.Columns)
			{
				case 1:
					return QGSHDD_INT1;

				case 2:
					return QGSHDD_INT2;

				case 3:
					return QGSHDD_INT3;

				case 4:
					return QGSHDD_INT4;
			}
			break;

		case D3D_SVT_FLOAT:
			//-- 테스트 더 필요
			switch (std.Rows)
			{
				case 1:	// 벡터 = 1x? 행렬
					switch (std.Columns)
					{
						case 1:
							return QGSHDD_FLOAT1;

						case 2:
							return QGSHDD_FLOAT2;

						case 3:
							return QGSHDD_FLOAT3;

						case 4:
							return QGSHDD_FLOAT4;
					}
					break;

				case 2:	// 2x? 행렬
					break;

				case 3:	// 3x? 행렬
					break;

				case 4:	// 4x? 행렬
					switch (std.Columns)
					{
						case 4:
							return QGSHDD_FLOAT16;
					}
					break;
			}
			break;
	}

	return QGSHDD_UNKNOWN;
}


//////////////////////////////////////////////////////////////////////////
// 자동 세이더 변수 처리
class D11ShaderWorker
{
	// friends 관계 때문에 namespace로 안하고 class로 함

private:
	static void OrthoProj(D11Device* dev, kpointer data, const qgShaderVar& v)
	{
		k_assert_eq(v.size, sizeof(tpl::Mat4));
		*(tpl::Mat4*)data = dev->_trfm.otm;
	}

	static void World(D11Device* dev, kpointer data, const qgShaderVar& v)
	{
		k_assert_eq(v.size, sizeof(tpl::Mat4));
		*(tpl::Mat4*)data = dev->_trfm.tm[QGTMS_WORLD];
	}

	static void View(D11Device* dev, kpointer data, const qgShaderVar& v)
	{
		k_assert_eq(v.size, sizeof(tpl::Mat4));
		*(tpl::Mat4*)data = dev->_trfm.tm[QGTMS_VIEW];
	}

	static void Proj(D11Device* dev, kpointer data, const qgShaderVar& v)
	{
		k_assert_eq(v.size, sizeof(tpl::Mat4));
		*(tpl::Mat4*)data = dev->_trfm.tm[QGTMS_PROJ];
	}

	static void ViewProj(D11Device* dev, kpointer data, const qgShaderVar& v)
	{
		k_assert_eq(v.size, sizeof(tpl::Mat4));
		*(tpl::Mat4*)data = dev->_trfm.vipr;
	}

	static void InvView(D11Device* dev, kpointer data, const qgShaderVar& v)
	{
		k_assert_eq(v.size, sizeof(tpl::Mat4));
		*(tpl::Mat4*)data = dev->_trfm.invv;
	}

	static void WorldViewProj(D11Device* dev, kpointer data, const qgShaderVar& v)
	{
		k_assert_eq(v.size, sizeof(tpl::Mat4));
		k_mat4_mul((tpl::Mat4*)data, &dev->_trfm.tm[QGTMS_WORLD], &dev->_trfm.vipr);
	}

	static void Tex0(D11Device* dev, kpointer data, const qgShaderVar& v)
	{
	}

	static void Tex1(D11Device* dev, kpointer data, const qgShaderVar& v)
	{
	}

	static void Tex2(D11Device* dev, kpointer data, const qgShaderVar& v)
	{
	}

	static void Tex3(D11Device* dev, kpointer data, const qgShaderVar& v)
	{
	}

	static void PropVec0(D11Device* dev, kpointer data, const qgShaderVar& v)
	{
		k_assert_eq(v.size, sizeof(tpl::Vec4));
		*(tpl::Vec4*)data = dev->_vdeb.propv[0];
	}

	static void PropVec1(D11Device* dev, kpointer data, const qgShaderVar& v)
	{
		k_assert_eq(v.size, sizeof(tpl::Vec4));
		*(tpl::Vec4*)data = dev->_vdeb.propv[1];
	}

	static void PropVec2(D11Device* dev, kpointer data, const qgShaderVar& v)
	{
		k_assert_eq(v.size, sizeof(tpl::Vec4));
		*(tpl::Vec4*)data = dev->_vdeb.propv[2];
	}

	static void PropVec3(D11Device* dev, kpointer data, const qgShaderVar& v)
	{
		k_assert_eq(v.size, sizeof(tpl::Vec4));
		*(tpl::Vec4*)data = dev->_vdeb.propv[3];
	}

	static void PropMat0(D11Device* dev, kpointer data, const qgShaderVar& v)
	{
		k_assert_eq(v.size, sizeof(tpl::Mat4));
		*(tpl::Mat4*)data = dev->_vdeb.propm[0];
	}

	static void PropMat1(D11Device* dev, kpointer data, const qgShaderVar& v)
	{
		k_assert_eq(v.size, sizeof(tpl::Mat4));
		*(tpl::Mat4*)data = dev->_vdeb.propm[1];
	}

	static void PropMat2(D11Device* dev, kpointer data, const qgShaderVar& v)
	{
		k_assert_eq(v.size, sizeof(tpl::Mat4));
		*(tpl::Mat4*)data = dev->_vdeb.propm[2];
	}

	static void PropMat3(D11Device* dev, kpointer data, const qgShaderVar& v)
	{
		k_assert_eq(v.size, sizeof(tpl::Mat4));
		*(tpl::Mat4*)data = dev->_vdeb.propm[3];
	}

	static void MatPalette(D11Device* dev, kpointer data, const qgShaderVar& v)
	{
		tpl::Mat4* m = (tpl::Mat4*)data;
		memcpy(m, dev->_vprn.wb_pcb, sizeof(tpl::Mat4) * dev->_vprn.wb_cnt);
	}

public:
	static qgShaderAutoInfo Autos[QGSHDA_MAX_VALUE];
};

// 목록
#define SV(m,t)	{0, "s" K_STRING(m), t, (qgShaderAutoFunc)&D11ShaderWorker::m}
qgShaderAutoInfo D11ShaderWorker::Autos[] =
{
	SV(OrthoProj, QGSHDA_ORTHO_PROJ),
	SV(World, QGSHDA_WORLD),
	SV(View, QGSHDA_VIEW),
	SV(Proj, QGSHDA_PROJ),
	SV(ViewProj, QGSHDA_VIEWPROJ),
	SV(InvView, QGSHDA_INVVIEW),
	SV(WorldViewProj, QGSHDA_WORLDVIEWPROJ),
	SV(Tex0, QGSHDA_TEX0),
	SV(Tex1, QGSHDA_TEX1),
	SV(Tex2, QGSHDA_TEX2),
	SV(Tex3, QGSHDA_TEX3),
	SV(PropVec0, QGSHDA_PROPVEC0),
	SV(PropVec1, QGSHDA_PROPVEC1),
	SV(PropVec2, QGSHDA_PROPVEC2),
	SV(PropVec3, QGSHDA_PROPVEC3),
	SV(PropMat0, QGSHDA_PROPMAT0),
	SV(PropMat1, QGSHDA_PROPMAT1),
	SV(PropMat2, QGSHDA_PROPMAT2),
	SV(PropMat3, QGSHDA_PROPMAT3),
	SV(MatPalette, QGSHDA_MATPALETTE),
};
#undef SV

//
void D11Shader::_InitAutoFunc()
{
	for (ksize_t i = 0; i < K_COUNTOF(D11ShaderWorker::Autos); i++)
		D11ShaderWorker::Autos[i].hash = k_strihash(D11ShaderWorker::Autos[i].name);
}

//
kpointer D11Shader::_MatchAutoFunc(qgShaderVar& v)
{
	for (ksize_t i = 0; i < K_COUNTOF(D11ShaderWorker::Autos); i++)
	{
		if (v.hash == D11ShaderWorker::Autos[i].hash &&
			v.name.CompareTo(D11ShaderWorker::Autos[i].name, true) == 0)
		{
			// 찾음
			v.role = QGSHDR_AUTO;
			v.intr = D11ShaderWorker::Autos[i].func;

			return NULL;
		}
	}

	// 없음
	v.role = QGSHDR_MANUAL;
	v.intr = NULL;

	return NULL;
}

#endif	// _QG_USE_D11
